package inits

import (
	g "app-webapi/comm/g"
	"app-webapi/comm/response"
	"app-webapi/controller"
	"app-webapi/entity"
	"errors"
	"fmt"
	"github.com/gin-contrib/sessions"
	"github.com/gin-contrib/sessions/cookie"
	"github.com/gin-gonic/gin"
	"github.com/google/uuid"
	"gorm.io/gorm"
	"math"
	"net"
	"net/http"
	"os"
	"runtime"
	"strings"
	"time"
)

func Route() {
	port, err := g.AppService.GetAppSetting("GinPort")
	engine := CreateEngine()
	// 最大上传文件大小 2G
	engine.MaxMultipartMemory = 2 << 30
	// 定义测试接口路由
	engine.GET("/", controller.Test)

	//// 健康检查
	//engine.GET("/healthy", controller.GetHealthy)
	//// 验证码获取
	//engine.GET("/captcha", controller.GetCaptcha)
	//// 登录接口
	//engine.POST("/login", controller.Login)
	//
	//// 下面的接口需要登录
	//engine.Use(AuthMiddle)

	// 路由组
	TestRoutes(engine)

	err = engine.Run(fmt.Sprintf(":%s", port))
	if err != nil {
		panic(err)
	}
}

func CreateEngine() *gin.Engine {

	engine := gin.Default()
	engine.Handlers = nil
	//engine.Use(traceId)
	//engine.Use(preLog)
	//engine.Use(afterLog)
	engine.Use(customRecovery)
	engine.Use(cors)

	// 404
	engine.NoRoute(func(c *gin.Context) {
		c.JSON(http.StatusNotFound, nil)
	})
	return engine

}
func cors(c *gin.Context) {
	method := c.Request.Method
	c.Header("Access-Control-Allow-Origin", "*")
	c.Header("Access-Control-Allow-Headers", "Content-Type,AccessToken,X-CSRF-Token, Authorization, Token,X-Token,X-User-ID")
	c.Header("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE, PUT")
	c.Header("Access-Control-Expose-Headers", "Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers, Content-Type")
	c.Header("Access-Control-Allow-Credentials", "true")

	// 放行所有OPTIONS方法
	if method == "OPTIONS" {
		c.AbortWithStatus(http.StatusNoContent)
	}

	// 处理请求
	c.Next()
}

func preLog(c *gin.Context) {
	value, _ := c.Get("Trace")
	remoteIp := c.GetHeader("X-Real-IP")
	if remoteIp == "" {
		remoteIp = c.Request.RemoteAddr
	}

	g.Logger.Infof("[%s] >>>>>>>>>>> [%s] %s From %s", value, c.Request.Method, c.Request.RequestURI, remoteIp)
	c.Next()
}
func afterLog(c *gin.Context) {
	defer func() {
		value, _ := c.Get("Trace")
		remoteIp := c.GetHeader("X-Real-IP")
		if remoteIp == "" {
			remoteIp = c.Request.RemoteAddr
		}
		g.Logger.Infof("[%s] <<<<< [%3d] [%s] %s To %s", value, c.Writer.Status(), c.Request.Method, c.Request.RequestURI, remoteIp)
	}()
	c.Next()

}

func customRecovery(c *gin.Context) {
	defer func() {
		if err := recover(); err != nil {
			// Check for a broken connection, as it is not really a
			// condition that warrants a panic stack trace.
			var brokenPipe bool
			if ne, ok := err.(*net.OpError); ok {
				var se *os.SyscallError
				if errors.As(ne, &se) {
					if strings.Contains(strings.ToLower(se.Error()), "broken pipe") || strings.Contains(strings.ToLower(se.Error()), "connection reset by peer") {
						brokenPipe = true
					}
				}
			}
			value, _ := c.Get("Trace")
			// 打印错误日志
			_, file, line, _ := runtime.Caller(2)
			for i := len(file) - 1; i > 0; i-- {
				if file[i] == '/' {
					file = file[i+1:]
					break
				}
			}
			devMsg := fmt.Sprintf("(%s:%d)", file, line)
			g.Logger.Errorf("[%s] %s %s", value, err, devMsg)

			if brokenPipe {
				// If the connection is dead, we can't write a status to it.
				c.Error(err.(error)) // nolint:
				c.Abort()
			} else {
				switch err.(type) {
				case string:
					response.Err("panic", err.(string)+devMsg, c)
					c.Abort()
					return
				case error:
					response.Err("panic", err.(error).Error()+devMsg, c)
					c.Abort()
					return
				default:
					response.Failed("panic", c)
					c.Abort()
					return
				}

			}
		}
	}()
	c.Next()

}

func traceId(c *gin.Context) {
	trace := uuid.NewString()
	c.Set("Trace", trace)
	c.Header("Trace", trace)
	c.Next()
}

// Session 中间件，处理session
func Session(keyPairs string) gin.HandlerFunc {
	store := SessionConfig()
	return sessions.Sessions(keyPairs, store)
}
func SessionConfig() sessions.Store {
	sessionMaxAge := 3600
	sessionSecret := "top-goer"
	var store sessions.Store
	store = cookie.NewStore([]byte(sessionSecret))
	store.Options(sessions.Options{
		MaxAge: sessionMaxAge, // seconds
		Path:   "/",
	})
	return store
}

func AuthMiddle(c *gin.Context) {
	// /favicon.ico 放行
	if c.Request.URL.Path == "/favicon.ico" {
		c.Next()
		return
	}

	// 内部接口放行
	ignoreToken, _ := g.AppService.GetAppSetting("IgnoreToken")
	if ignoreToken == "true" || strings.HasPrefix(c.Request.URL.Path, "/internalApi") {
		user := &entity.Userinfo{
			Model:        gorm.Model{ID: math.MaxInt},
			Username:     "whosyourdaddy",
			RealName:     "whosyourdaddy",
			IsSuperAdmin: true,
			CreateUserId: math.MaxInt,
		}
		c.Set("user", user)
		c.Next()
		return
	}
	token := c.GetHeader("token")
	if token == "" {
		c.JSON(http.StatusUnauthorized, gin.H{"msg": "token不能为空"})
		c.Abort()
		return
	}

	user := &entity.Userinfo{}
	g.Db.Model(user).Where("token = ?", token).Find(user)
	if user.ID == 0 {
		c.JSON(http.StatusUnauthorized, gin.H{"msg": "token无效"})
		c.Abort()
		return
	}
	// 校验用户过期时间 到期时间小于现在24小时
	if !user.ExpireDate.IsZero() && user.ExpireDate.Add(24*time.Hour).Before(time.Now()) {
		c.JSON(http.StatusUnauthorized, gin.H{"msg": "用户已过期"})
		c.Abort()
		return
	}
	c.Set("user", user)
	c.Next()
	return
}
